/*
  Copyright © 2010  Rodrigo Peixoto
  Copyright © 2010  Camilo Costa Campos
  Copyright © 2010  Willian Victor Silva

      This file is part of Touché.

      Touché is free software: you can redistribute it and/or modify
      it under the terms of the GNU Lesser General Public License as published
      by the Free Software Foundation, either version 3 of the License, or
      (at your option) any later version.

      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU Lesser General Public License for more details.

      You should have received a copy of the GNU Lesser General Public License
      along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */

#include "manager.h"
#include "interpreter.h"
#include "writer.h"
#include "QXmppMessage.h"
#include "QXmppRoster.h"
#include "QXmppPacket.h"
#include <QMessageBox>
#include <iostream>
using namespace Touche;

ColaborativeManager::ColaborativeManager(QObject *parent) :
        m_client(new QXmppClient(parent)),
        m_connected(false)
{
    //Connection status and error signals -------------------------------------
    connect(this->m_client, SIGNAL(connected()), this, SLOT(setAsConnected()));
    connect(this->m_client, SIGNAL(disconnected()),
            this, SLOT(setAsDisconnected()));
    connect(this->m_client, SIGNAL(error(QXmppClient::Error)),
            this, SIGNAL(connectionError(QXmppClient::Error)));

    //Presence and Message control flow ---------------------------------------
    connect(this->m_client, SIGNAL(messageReceived(QXmppMessage)),
            this, SIGNAL(messageReceived(QXmppMessage)));



    connect(this->m_client,SIGNAL(presenceReceived(QXmppPresence)),
            this, SLOT(onPresenceReceived(QXmppPresence)));


    //Contacts handle ---------------------------------------------------------
    connect(&this->m_client->getRoster(), SIGNAL(rosterReceived()),
            this, SLOT(updateContacts()));
    //connect(&this->m_client->getRoster(), SIGNAL(rosterChanged(QString)),
    //        this, SLOT(updateContacts()));
    connect(&this->m_client->getRoster(),
            SIGNAL(presenceChanged(QString,QString)),
            this, SLOT(presenceChanged(QString,QString)));   
    //------------------------------------------------------------------------

}

ColaborativeManager::~ColaborativeManager()
{
    this->m_client->disconnect();
}

void ColaborativeManager::connectToServer(QString login, QString passwd)
{
    this->m_login = login;
    this->m_password = passwd;
    QXmppConfiguration config;
    QXmppPresence initialPresence;
    initialPresence.setType(QXmppPresence::Available);
    config.setResource(QString::fromUtf8("Touché"));
    config.setAutoAcceptSubscriptions(false);
    config.setPasswd(this->m_password);
    config.setUser(this->m_login);
    config.setHost("talk.google.com");
    config.setDomain("gmail.com");
    this->m_client->connectToServer(config, initialPresence);
    qDebug("Trying to connect to gmail...");
}

bool ColaborativeManager::isOnline(QString bareJid)
{
    /*Verifica se a lista de presencas do m_client possui algo,
    caso seja vazia, o usuário estáa offline. */
    return !this->m_client->getRoster().getAllPresencesForBareJid(bareJid).isEmpty();
}

QMap<QString, Channel*> ColaborativeManager::channels()
{
    return this->m_channels;
}

bool ColaborativeManager::isConnected()
{
    return this->m_connected;
}

void ColaborativeManager::setAsConnected()
{
    qDebug("Connected to gmail");
    this->m_connected = true;
}

void ColaborativeManager::setAsDisconnected()
{
    qDebug("Disconnected from gmail");
    this->m_connected = false;
}

void ColaborativeManager::disconnectAccount()
{
    this->m_client->disconnect();
    this->setAsDisconnected();
}

QStringList ColaborativeManager::contacts()
{
    return this->m_contacts;
}

void ColaborativeManager::setContacts(QStringList new_contacts)
{
    this->m_contacts = new_contacts;
}

void ColaborativeManager::clearContacts()
{
    this->m_contacts.clear();
}

void ColaborativeManager::updateContacts()
{
    QStringList newContacts;
    this->clearContacts();
    newContacts.clear();
    foreach(QString bareJid, this->m_client->getRoster().getRosterBareJids()){
        if (this->isOnline(bareJid)){
            newContacts.append(bareJid);
        }
        this->setContacts(newContacts);
    }
}

QStringList ColaborativeManager::allBuddies(){
    QStringList allBuddies;
    QMap<QString, QXmppRoster::QXmppRosterEntry> entries = this->m_client->getRoster().getRosterEntries();

    foreach(QString bareJid, this->m_client->getRoster().getRosterBareJids()){
        qDebug(qPrintable(bareJid));
        if(!allBuddies.contains(bareJid)&&((entries.value(bareJid).getSubscriptionType())==QXmppRoster::QXmppRosterEntry::Both))
            allBuddies.append(bareJid);
    }
    return allBuddies;
}

void ColaborativeManager::presenceChanged(QString bareJid, QString resource)
{
    this->removeUserFromChannels(bareJid);

    if(this->isOnline(bareJid)){
        if(!this->m_contacts.contains(bareJid)){
            this->m_contacts.append(bareJid);
            emit this->contactsChanged();
        }
        QXmppPresence presence = this->m_client->getRoster()
                                 .getPresence(bareJid, resource);






        ColaborativeXmlReader * channel_xml \
                = new ColaborativeXmlReader(presence.getStatus()
                                            .getStatusText());
        QPair<QString, QString> channelnamepasswd = channel_xml->loadChannel();
        if(!channelnamepasswd.first.isEmpty()){
            if(this->m_channels.contains(channelnamepasswd.first)){ //if channel already exists
                if(!this->m_channels[channelnamepasswd.first]->hasUser(bareJid)){
                    this->m_channels[channelnamepasswd.first]->addUser(bareJid);
                    //this->m_client->sendMessage(bareJid, "you are joined to channel");
                }
            } else { //if channel does not exists
                Channel * chan = new Channel(channelnamepasswd);
                chan->addUser(bareJid);
                this->m_channels[channelnamepasswd.first] = chan;
            }
            emit this->channelsChangend();
        }
    } else {
        this->m_contacts.removeOne(bareJid);
        emit this->contactsChanged();
    }
        emit this->channelsChangend();
}

void ColaborativeManager::onPresenceReceived(QXmppPresence presence)
{
    if (presence.getType()==QXmppPresence::Subscribe)
    {


            int role;
            QString bareJid = presence.from();

            QMessageBox dialog;
            dialog.setIcon(QMessageBox::Question);
            dialog.setText("The user " + bareJid +" wants add you");
            dialog.setWindowTitle("New buddy request");
            dialog.addButton("Accept",QMessageBox::AcceptRole);
            dialog.addButton("Deny",QMessageBox::RejectRole);
            role = dialog.exec();
            if(role==QMessageBox::AcceptRole){
                qDebug("aceitou %d", role);
                addBuddy(bareJid);
            }else{
                qDebug("rejeitou %d", role);
                removeBuddy(bareJid);
            }


    }
}


void ColaborativeManager::removeUserFromChannels(QString bareJid)
{
    foreach(Channel * chan, this->m_channels){
        if(chan && chan->hasUser(bareJid)){
            chan->removeUser(bareJid);
            qDebug("User %s is leaving channel %s.", qPrintable(bareJid), qPrintable(chan->name()));
            if(chan->isEmpty()){
                qDebug("Closing the channel %s", qPrintable(chan->name()));
                this->m_channels.remove(chan->name());
                foreach(QString name, this->channels().keys()){
                    qDebug(qPrintable(name));
                }
            }
        }
    }
    emit this->channelsChangend();
}

void ColaborativeManager::sendMessageToUser(QString bareJid, QString message)
{
    message = Writer::writeText(message); //adiciona a tag <toucheText>
    qDebug(qPrintable(message));
    this->m_client->sendMessage(bareJid, message);
}

void ColaborativeManager::sendMessageToUser(QXmppMessage message)
{
    this->m_client->sendPacket(message);
}

void ColaborativeManager::sendMessageToChannel(QString channelName, QString message)
{
    //message = Writer::writeText(message); //adiciona a tag <toucheText>
    foreach(QString recipient, this->m_channels[channelName]->usersBarejid()){
        this->sendMessageToUser(recipient, message);
    }
}

void ColaborativeManager::sendMessageToChannel(QXmppMessage message)
{
//    foreach(QString recipient, this->m_channels[channelName]->usersBarejid()){
//        this->sendMessageToUser(message);
//    }
}


void ColaborativeManager::addBuddy(const QString& bareJid)
{
    QXmppPresence packet;
    packet.setTo(bareJid);
    packet.setType(QXmppPresence::Subscribe);
    this->m_client->sendPacket(packet);

    packet.setType(QXmppPresence::Subscribed);
    this->m_client->sendPacket(packet);
    qDebug("adicionado novo buddy");
    this->updateContacts();



}


//remover buddy
void ColaborativeManager::removeBuddy(const QString& bareJid)
{

//BUG !!! NÃO ATUALIZA A LISTA DE CONTATOS ATÉ SER DESCONECTADO

    QXmppPresence packet;
    packet.setTo(bareJid);
    packet.setType(QXmppPresence::Unsubscribed);
    this->m_client->sendPacket(packet);

    packet.setType(QXmppPresence::Unsubscribe);
    this->m_client->sendPacket(packet);
    qDebug("removendo buddy");

    this->updateContacts();

}






#include <QCryptographicHash>

void ColaborativeManager::createChannel(QString channel, QString channelPass){
    this->m_channel = channel;
    this->m_channelPass = channelPass;
    if(!channelPass.isEmpty()){
        QCryptographicHash hasher(QCryptographicHash::Md5);
        hasher.addData(channelPass.toUtf8());
        qDebug()<< hasher.result();
        this->m_channelPass = hasher.result().toHex();
        qDebug()<<m_channelPass;
        this->m_client->setClientPresence("<touchesession name=\""+m_channel+"\">"+m_channelPass+"</touchesession>");
    }else{
        this->m_client->setClientPresence("<touchesession name=\""+m_channel+"\"/>");
    }
}

bool ColaborativeManager::enterChannel(QString channel, QString channelPass){
    QCryptographicHash hasher(QCryptographicHash::Md5);
    hasher.addData(channelPass.toUtf8());
    if(channelPass.isEmpty()){
        if(this->m_channels[channel]->password().isEmpty()){
            this->m_client->setClientPresence("<touchesession name=\""+channel+"\">"+hasher.result().toHex()+"</touchesession>");
            this->m_channel = channel;
            this->m_channelPass = channelPass;
            return true;
        }
    }else if(this->m_channels[channel]->password().toUtf8() == hasher.result().toHex()){
        this->m_client->setClientPresence("<touchesession name=\""+channel+"\">"+hasher.result().toHex()+"</touchesession>");
        this->m_channel = channel;
        this->m_channelPass = channelPass;
        return true;
    }
    return false;
}
QString ColaborativeManager::getChannel(){
    return this->m_channel;
}

QString ColaborativeManager::getLogin(){
    return this->m_login;
}

void ColaborativeManager::clearChannel(){
    this->m_channel.clear();
}

void ColaborativeManager::lockElement(const QUuid &uuid)
{
    QString message = Writer::lockElement(uuid); //adiciona a tag <LockElement>
    if(Channel *channel = m_channels[m_channel]){
        foreach(QString bareJid, channel->usersBarejid()){
            qDebug(qPrintable(bareJid));
            this->m_client->sendMessage(bareJid, message);
        }
    }
}

void ColaborativeManager::unlockElement(const QUuid &uuid)
{
    {
        QString message = Writer::unlockElement(uuid); //adiciona a tag <UnlockElement>
        if(Channel *channel = m_channels[m_channel]){
            foreach(QString bareJid, channel->usersBarejid()){
                qDebug(qPrintable(bareJid));
                this->m_client->sendMessage(bareJid, message);
            }
        }
    }
}

void ColaborativeManager::onElementAddedByMe(Touche::Element *element)
{
    qDebug("adicionando elemento na casa da mãe joana");
    if(Channel *channel = m_channels[m_channel]){
        foreach(QString bareJid, channel->usersBarejid()){
            this->m_client->sendMessage(bareJid, Writer::addElement(element->id, element->type(), element->save()));
        }
    }
}

void ColaborativeManager::onLinkAddedByMe(const QUuid &uuid1 , const QUuid &uuid2)
{
    qDebug("adicionando LINK na casa da mãe joana");
    if(Channel *channel = m_channels[m_channel]){
        foreach(QString bareJid, channel->usersBarejid()){
            this->m_client->sendMessage(bareJid, Writer::addLink(uuid1,uuid2));
        }
    }
}
